package jdsl.core.ref;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Map;

import jdsl.core.api.Sequence;
import jdsl.core.ref.ArraySequence;

/**
 * This is a class that implements the BNF's definition of a node, that is, the parts
 * a tree consists of. It contains an ID implemented as a String and a Map implemented
 * by an Hashtable to store the attributes, implemented by the Attribute class, relating
 * to the node. 
 * 
 * According to the BNF a node does not need to have any attributes:
 * tree ::= ID { attr } [ LPAR { tree } RPAR ]
 *          -----------
 * 
 * where the underlined portion is a node.
 * 
 * Thus the Hashtable does not necessarily contain anything.
 * 
 * Strictly according to the BNF a node does not have children. Instead trees are attached
 * to the node. For practical purposes this is equivalent to a node with children.
 * 
 * @author Lucas Dreyer
 */
public class NodeElement {
	private String ID;
	private Map<String, Attribute> attributes;
	
	/**
	 * Default Constructor
	 */
	public NodeElement() {
		attributes = new Hashtable<String, Attribute>();
	}
	
	/**Extended Constructor
	 * @param ident: The ID or identifier that describes this node. 
	 * */
	public NodeElement(String ident) {
		ID = ident;
		attributes = new Hashtable<String, Attribute>();
	}
	
	/**Extended Constructor
	 * @param ident: The ID or identifier that describes this node. 
	 * @param map: A map of Attribute objects.
	 * */
	public NodeElement(String ident, Map<String, Attribute> map) {
		ID = ident;
		attributes = map;
	}
	
	/**
	 * Returns whether or not this node has any attributes.
	 */
	public boolean hasAttributes() {
		return !attributes.isEmpty();
	}
	
	/**
	 * Returns the attributes associated with this node.
	 */
	public Map<String, Attribute> getAttributes() {
		return attributes;
	}
	
	/**
	 * Returns the NodeElement's ID or identifier.
	 */
	public String getID() {
		return ID;
	}
	
	/**
	 * Returns the Attribute corresponding to the specified String.
	 */
	public Attribute getAttribute(String ID1) {
		return attributes.get(ID1);
	}
	
	/**
	 * Returns whether the node has a certain Attribute.
	 * @param ID1: The ID1, or 'describing ID' of the Attribute.
	 * @return True if the Attribute exists at the node.
	 */
	public boolean containsID(String ID1) {
		return attributes.containsKey(ID1);
	}
	
	/**
	 * Change the map of attributes associated with this node.
	 */
	public void setAttributes(Map<String, Attribute> map) {
		attributes = map;
	}
	
	/**
	 * Adds a attribute to the map of attributes associated with this node.
	 */
	public void addAttribute(Attribute attr) {
		attributes.put(attr.getID1(), attr);
	}
	
	/**
	 * Converts the Map at the node to a array of type Attribute. For example
	 * convertToArray()[i] refers to the i'th Attribute at the node. The order
	 * that the Attribute objects were originally added to the Map seems to be held.
	 * @return An array of all Attribute objects at the node.
	 */
	public Attribute[] convertToArray() {
		Attribute[] list = new Attribute[attributes.size()];
		attributes.values().toArray(list);
		return list;
	}
	
	/**
	 * Returns a string in the format described by the BNF:
	 * 
	 * tree ::= ID { attr } [ LPAR { tree } RPAR ]
	 *          -----------
	 *          
	 * where the underlined portion is a node. Therefore:
	 * 
	 * ID attr
	 * 
	 * For example if ID was "def" the output would be:
	 * def attribute1 attribute2 attribute3 attribute4... attributeN
	 * 
	 */
	public String toString() {
		String output;
		output = ID;
		for (int i = 0; i < attributes.size(); i++) {
			output = output + " " + convertToArray()[i];
		}
		return output;
	}
}

